Appearance
前言
很多时候,攻击都是来源自一些未知的代码执行,比如eval的错误执行、new Function。本文尽可能列举所有的有安全风险的操作来避免业务犯错。
eval
eval是一个常见的动态运行代码的方式,eval的作用域和函数作用域类型,适用于当前的所在的作用域。eval存在两个地方,第一个是全局变量,第二个是全局空间中。简单重写
js
tempEval = eval; // 保存旧eval
window.eval = eval = () => {
upload() // 上报调用行为
throw new Error('not allow eval')
}new Function 和 constructor
有些时候代码可能是通过new Function生成,或者constructor()方式。这种方式申明的内容总是全局作用域,而是在当前作用域之下。
js
const action = 'log'
const functionContent = `console.${action}('xxxx')`
const someFunction = new Function(functionContent)
const someFunction1 = (function(){}).constructor(functionContent); // 也能实现对直接重写
js
const tempFunction = Function;
window.Function = Function = function() {
upload();
throw new Error('not allow new Function')
}
// 对于constructor的方式有如下
Function.constructor === Function.prototype.constructor // true
Object.defineProperty(Function.prototype, 'constructor', {
get() {
return () => {
upload();
throw new Error('not allow call function constructor')
}
}
})setTimeout 和 setInterval
setTimeOut和setInterval中第一个参数出入字符时,也会动态执行执行,需要避免第一个参数错误。
js
const tempSetTimeout = setTimeout;
setTimeout = function(fn) {
const restArgs = Array.prototype.slice.call(arguments, 1);
if(typeof fn !== 'function') {
// upload()
throw new Error('not allow call function constructor')
}
return tempSetTimeout.apply(fn, restArgs)
}JavaScript动态创建
通过JavaScript动态创建script标签也是一种常见的规避eval的方式,这一点在安全SDK中通过拦截可疑脚本实现,创建内联的脚本。
js
const scriptContent = `console.log(${123})`;
const scriptElement = document.createElement('script');
scriptElement.textContent = scriptContent;
document.body.appendChild(scriptElement);Blob文件动态创建
创建一个blob对象,然后URL.createObjectUrl()转换成url,通过script标签加载。(这种是常见的攻击方式)
js
const scriptContent = `console.log(${123})`;
const blob = new Blob([scriptContent], { type: 'application/javascript' });
const scriptElement = document.createElement('script');
scriptElement.src = URL.createObjectUrl(blob);
document.body.appendChild(scriptElement);通过创建iframe绕过
创建一个iframe标签,不添加url,默认情况iframe能够操作父页面所有信息,也能实现动态脚本。防不胜防啊
js
const iframe = document.createElement('iframe');
document.body.append(iframe);
iframe.contentDocument.write(`
parent.localStorage // iframe获取到父页面的数据
`)上面几种方式都是添加型的,全局重写appendChild函数就能检查,不过SDK来说这种方式也不容易。现在是spa的添加,会有大量script标签被添加,很难规避。不过也有好处,大部分spa框架在xss等方面做的很好。开发严格遵守的情况还是很难出现xss等行为。
后面将如何拦截脚本这些行为
